package com.lagou.edu.factory;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;

import java.io.File;
import java.io.FileFilter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

/**
 * 一、编程题
 *
 * 学员自定义@Service、@Autowired、@Transactional注解类，
 * 完成基于注解的IOC容器（Bean对象创建及依赖注入维护）和声明式事务控制，
 * 写到转账工程中，并且可以实现转账成功和转账异常时事务回滚
 *
 * 注意考虑以下情况：
 *
 *  1）注解有无value属性值【@service（value=""） @Repository（value=""）】
 *
 *  2）service层是否实现接口的情况【jdk还是cglib】
 *
 * 二、作业资料说明：
 *
 * 1、提供资料：代码工程、验证及讲解视频、SQL脚本。
 *
 * 2、讲解内容包含：题目分析、实现思路、代码讲解。
 *
 * 3、效果视频验证
 *
 *    1）实现转账成功和转账异常时事务回滚。
 *
 *    2）展示和讲解自定义@Service、@Autowired、@Transactional注解类。
 */
public class AnnotationBeanFactory {

    // 存储对象
    private static Map<String,Object> map = new HashMap<>();

    //service加载路径
    private static String[]  paths = {"com.lagou.edu.service.impl","com.lagou.edu.dao.impl","com.lagou.edu.utils","com.lagou.edu.factory"};

    //Transactional加载路径
    private static String[]  transPaths = {"com.lagou.edu.service.impl"};

    static {
        ClassLoader classLoader = AnnotationBeanFactory.class.getClassLoader();

        //第一步 加载配置注解Service的对象存放入容器
        for(String path : paths){
            URI url = null;
            try {
                url = classLoader.getResource(path.replace(".","/")).toURI();
            } catch (URISyntaxException e) {
                e.printStackTrace();
            }
            File file = new File(url);
            file.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    if(pathname.getName().endsWith(".class")){
                        Class<?> clazz = null;
                        try {
                            clazz = classLoader.loadClass(path + "." + pathname.getName().replace(".class", ""));
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                        if(clazz.getAnnotation(Service.class)!=null){
                            Annotation annotation = clazz.getAnnotation(Service.class);
                            Object object=null;
                            try {
                                object = Class.forName(clazz.getName()).newInstance();
                            } catch (ClassNotFoundException e) {
                                e.printStackTrace();
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            } catch (InstantiationException e) {
                                e.printStackTrace();
                            }
                            map.put(((Service) annotation).value(),object);
                        }
                        return true;
                    }
                    return false;
                }
            });

        }

        //扫描配置Autowired标签的属性值，从容器中获取对于的Bean并通过set方法设置
        for(String path : paths){
            URI url = null;
            try {
                url = classLoader.getResource(path.replace(".","/")).toURI();
            } catch (URISyntaxException e) {
                e.printStackTrace();
            }
            File file = new File(url);
            file.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    if(pathname.getName().endsWith(".class")){
                        Class<?> clazz;
                        try {
                            clazz = classLoader.loadClass(path + "." + pathname.getName().replace(".class", ""));
                            if(clazz.getAnnotation(Service.class)!=null){
                                Annotation annotation = clazz.getAnnotation(Service.class);
                                Object object = map.get(((Service) annotation).value());
                                Field[] fields = object.getClass().getDeclaredFields();
                                for(Field field:fields){
                                    Annotation fieldAnnotation = field.getAnnotation(Autowired.class);
                                    if(null != fieldAnnotation){
                                        String fieldName = field.getName();
                                        Method[] methods = object.getClass().getMethods();
                                        for(Method method:methods){
                                            if(method.getName().equalsIgnoreCase("set"+fieldName)){
                                                method.invoke(object,map.get(fieldName));
                                            }
                                        }
                                    }
                                }
                                map.put(((Service) annotation).value(),object);
                            }
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                        return true;
                    }
                    return false;
                }
            });
        }

        //第三步 扫描配置Transactional注解的类，通过动态代理增强Bean实例；
        for(String path : transPaths){
            URI url = null;
            try {
                url = classLoader.getResource(path.replace(".","/")).toURI();
            } catch (URISyntaxException e) {
                e.printStackTrace();
            }
            File file = new File(url);
            file.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    if(pathname.getName().endsWith(".class")){
                        Class<?> clazz = null;
                        try {
                            clazz = classLoader.loadClass(path + "." + pathname.getName().replace(".class", ""));
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                        if(clazz.getAnnotation(Service.class)!=null && clazz.getAnnotation(Transactional.class)!=null){

                            try {
                                Annotation annotationService = clazz.getAnnotation(Service.class);
                                Method[] methods = map.get("proxyFactory").getClass().getMethods();
                                for(Method method:methods){
                                    if(method.getName().equalsIgnoreCase("getJdkProxy")){
                                        Object object = method.invoke(map.get("proxyFactory"),map.get(((Service) annotationService).value()));
                                        map.put(((Service) annotationService).value(),object);
                                    }
                                }
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }  catch (InvocationTargetException e) {
                                e.printStackTrace();
                            }

                        }
                        return true;
                    }
                    return false;
                }
            });

        }
        System.out.println(map.toString());
    }


    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static  Object getBean(String id) {
        return map.get(id);
    }

}
